import { store } from "@/redux";
import { AppRouteModule, AppRouteRecordRaw } from "@/routers/types";
import { isHttpUrl } from "@/utils/is";
import { cloneDeep, filter, omit } from "lodash-es";
import { EXCEPTION_COMPONENT, getParentLayout, LayoutIndex } from "@/routers/constant";
import lazyLoad from "@/routers/utils/lazyLoad";
import React, { FunctionComponentElement } from "react";
import { type RouteObject } from "react-router-dom";
import { joinParentPath } from "@/routers/utils/menuHelper";

// const IFRAME = () => import("@/views/base/iframe/FrameBlank.vue");
const LayoutMap = new Map<string, FunctionComponentElement<any>>();
LayoutMap.set("LAYOUT", React.createElement(LayoutIndex));
// LayoutMap.set("IFRAME", Exception);

let dynamicViewsModules: Record<string, () => Promise<{ default: any }>>;

// Dynamic introduction
export function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) {
	dynamicViewsModules = dynamicViewsModules || import.meta.glob("/src/views/**/*.tsx");
	if (!routes) return;
	routes.forEach(item => {
		if (!item.component && item.meta?.frameSrc) item.component = "IFRAME";

		const { component, name } = item;
		const { children } = item;
		if (component) {
			const layoutFound = LayoutMap.get((component as string).toUpperCase());
			if (layoutFound) item.component = layoutFound;
			else item.element = dynamicImport(dynamicViewsModules, component as string, !!item.keepAlive, component as string);
		} else if (name) {
			item.element = getParentLayout();
		}
		const meta = item.meta || {};
		meta.title = item.name;
		meta.icon = item.icon;
		meta.hideMenu = !item.visible;
		meta.orderNo = item.sort;
		meta.ignoreKeepAlive = !item.keepAlive;
		item.meta = meta;
		item.name = item.name =
			item.componentName && item.componentName.length > 0 ? item.componentName : toCamelCase(item.path, true);
		children && asyncImportRoute(children);
	});
}

function getRoutePathByComponentPath(component: string) {
	const startFlag = component.startsWith("/");
	const endFlag = component.endsWith("/index");
	if (!startFlag) component = "/" + component;
	const lastIndex = endFlag ? component.lastIndexOf("/") : component.length;
	return component.substring(0, lastIndex);
}

function dynamicImport(
	dynamicViewsModules: Record<string, () => Promise<{ default: any }>>,
	component: string,
	keepAlive: boolean
) {
	const keys = Object.keys(dynamicViewsModules);
	const matchKeys = keys.filter(key => {
		const k = key.replace("/src/views", "");
		const startFlag = component.startsWith("/");
		const endFlag = component.endsWith(".tsx");
		const startIndex = startFlag ? 0 : 1;
		const lastIndex = endFlag ? k.length : k.lastIndexOf(".");
		return k.substring(startIndex, lastIndex) === component;
	});
	if (matchKeys?.length === 1) {
		const matchKey = matchKeys[0];
		return lazyLoad(dynamicViewsModules[matchKey], keepAlive, getRoutePathByComponentPath(component));
	} else if (matchKeys?.length > 1) {
		// console.warn(
		// 	"Please do not create `.TSX` files with the same file name in the same hierarchical directory under the views folder. This will cause dynamic introduction failure"
		// );
	} else {
		// console.warn(`在src/views/下找不到\`${component}.tsx\`, 请自行创建!`);
		return lazyLoad(EXCEPTION_COMPONENT);
	}
}

// Turn background objects into routing objects
export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModule[]): T[] {
	routeList.forEach((route: AppRouteModule) => {
		if (isHttpUrl(route.path as string)) route.component = "IFrame";
		else if (route.children && route.parentId === 0) route.component = "LAYOUT";
		else if (!route.children) route.component = route.component as string;
		const component = route.component as string;
		if (component) {
			const meta = route.meta || {};
			meta.hideMenu = !route.visible;
			meta.orderNo = route.sort;
			meta.ignoreKeepAlive = !route.keepAlive;
			meta.title = route.name;
			meta.icon = route.icon;
			if (component.toUpperCase() === "LAYOUT") {
				// react-router中element属性使用组件标签而不是函数，如<Component />
				route.element = LayoutMap.get("LAYOUT".toUpperCase());
			} else if (component.toUpperCase() === "IFRAME") {
				route.component = LayoutMap.get("IFRAME".toUpperCase());
			} else {
				// 处理顶级非目录路由
				meta.single = true;
				route.children = [cloneDeep(route)];
				route.element = LayoutMap.get("LAYOUT".toUpperCase());
				route.name = `${toCamelCase(route.path, true)}Parent`;
				route.path = "";
			}
			route.meta = meta;
		} else {
			// console.warn(`请正确配置路由：${route?.name}的component属性`);
		}
		route.children && asyncImportRoute(route.children);
	});
	return routeList as unknown as T[];
}

/**
 * Convert multi-level routing to level 2 routing
 */
export function flatMultiLevelRoutes(routeModules: AppRouteModule[]) {
	const modules: AppRouteModule[] = cloneDeep(routeModules);

	for (let index = 0; index < modules.length; index++) {
		const routeModule = modules[index];
		// 判断级别是否 多级 路由
		if (!isMultipleRoute(routeModule)) {
			// 声明终止当前循环， 即跳过此次循环，进行下一轮
			continue;
		}
		// 路由等级提升
		promoteRouteLevel(routeModule);
	}
	return modules;
}

function joinChildren(routes: any[], children: any = []) {
	if (!children || !children.length) return;
	for (let index = 0; index < children.length; index++) {
		const child = children[index];
		routes.push(child);
		if (child?.children?.length) joinChildren(routes, child.children);
	}
	return routes;
}

// Routing level upgrade
function promoteRouteLevel(routeModule: AppRouteModule) {
	joinParentPath([routeModule]);
	let routers = joinChildren([], routeModule.children);
	addToChildren(routers, routeModule.children || [], routeModule);
	routers = [];
	// omit lodash的函数 对传入的item对象的children进行删除
	routeModule.children = routeModule.children?.map(item => omit(item, "children"));
}

// Add all sub-routes to the secondary route
function addToChildren(routes: RouteObject[], children: AppRouteRecordRaw[], routeModule: AppRouteModule) {
	for (let index = 0; index < children.length; index++) {
		const child = children[index];
		const route = routes.find(item => item.name === child.name);
		if (!route) continue;

		routeModule.children = routeModule.children || [];
		if (!routeModule.children.find(item => item.name === route.name))
			routeModule.children?.push(route as unknown as AppRouteModule);

		if (child.children?.length) addToChildren(routes, child.children, routeModule);
	}
}

// Determine whether the level exceeds 2 levels
// 判断级别是否超过2级
function isMultipleRoute(routeModule: AppRouteModule) {
	// Reflect.has 与 in 操作符 相同, 用于检查一个对象(包括它原型链上)是否拥有某个属性
	if (!routeModule || !Reflect.has(routeModule, "children") || !routeModule.children?.length) return false;

	const children = routeModule.children;

	let flag = false;
	for (let index = 0; index < children.length; index++) {
		const child = children[index];
		if (child.children?.length) {
			flag = true;
			break;
		}
	}
	return flag;
}

function toCamelCase(str: string, upperCaseFirst: boolean) {
	str = (str || "")
		.replace(/-(.)/g, (group1: string) => {
			return group1.toUpperCase();
		})
		.replaceAll("-", "");

	if (upperCaseFirst && str) str = str.charAt(0).toUpperCase() + str.slice(1);

	return str;
}

// 构建路由
export const buildRoutesAction = (): AppRouteRecordRaw[] => {
	const { user: userStore } = store.getState();
	if (!userStore.userInfo) return [];

	let routes: AppRouteRecordRaw[] = [];
	// const roleList = userStore.getRoleList || [];
	const userInfo = userStore.userInfo || {};

	// 路由过滤器 在 函数filter 作为回调传入遍历使用
	// const routeFilter = (route: AppRouteRecordRaw) => {
	// 	const { meta } = route;
	// 	// 抽出角色
	// 	const { roles } = meta || {};
	// 	if (!roles) return true;
	// 	// 进行角色权限判断
	// 	return roleList.some(role => roles.includes(role));
	// };

	const routeRemoveIgnoreFilter = (route: AppRouteRecordRaw) => {
		const { meta } = route;
		// ignoreRoute 为true 则路由仅用于菜单生成，不会在实际的路由表中出现
		const { ignoreRoute } = meta || {};
		// arr.filter 返回 true 表示该元素通过测试
		return !ignoreRoute;
	};

	let routeList: AppRouteRecordRaw[] = [];
	try {
		routeList = cloneDeep(userInfo.menus) as AppRouteRecordRaw[];
	} catch (error) {
		console.error(error);
	}
	if (!routeList || !routeList.length) return [];
	// Dynamically introduce components
	routeList = transformObjToRoute(routeList);
	// remove meta.ignoreRoute item
	routeList = filter(routeList, routeRemoveIgnoreFilter);
	routeList = routeList.filter(routeRemoveIgnoreFilter);
	routeList = flatMultiLevelRoutes(routeList);
	routes = [...routeList];

	return routes;
};
